package gui.actions;


import com.google.inject.Inject;
import gui.Controller;
import gui.ErrorDialog;
import gui.TourneyManView;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import tournamentmanager.*;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Actions {
    private final Controller controller;
    private final TourneyManView view;
    private final Preferences model;
    private final TournamentLauncher tournamentLauncher;

    @Inject
    public Actions(Controller controller, TourneyManView view, Preferences model, TournamentLauncher tournamentLauncher) {
        this.controller = controller;
        this.view = view;
        this.model = model;
        this.tournamentLauncher = tournamentLauncher;
    }

    public void init() {
        initListeners();
        initViewers();
        initTimeControlTypesCombo();
        controller.loadViewFromModel();
    }

    private void initListeners() {
        view.getEngineSettings().getBtnPlay().addSelectionListener(getBtnPlayListener());
        view.btnAddEngine.addSelectionListener(getBtnAddEngineListener());
        view.lstPreconfEngines.addSelectionListener(getPreConfEnginesSelectedListener());
        view.btnAddToTournament.addSelectionListener(getBtnAddToTrnListener());
        view.btnRemoveFromTrn.addSelectionListener(getBtnRemoveFromTrnListener());
        view.getEngineSettings().getBtnSaveEngine().addSelectionListener(getBtnSaveEngineListener());
        view.getEngineSettings().getBtnRemoveEngine().addSelectionListener(getBtnRemoveEngineListener());
        view.btnLoad.addSelectionListener(getBtnLoadListener());
        view.btnRunTrn.addSelectionListener(tournamentLauncher.getRunTournamentButton());
        view.btnSaveTrn.addSelectionListener(getBtnSaveTrnListener());
        view.btnSaveTrnAs.addSelectionListener(getBtnSaveTrnAsListener());
        view.getEngineSettings().getBtnBrowseEngineDir().addSelectionListener(getBtnBrowseEngineDirListener());
        view.getEngineSettings().getBtnBrowseEngineCmd().addSelectionListener(getBtnBrowseEngineCmdListener());
        view.btnBrowseOutPGNFile.addSelectionListener(getBtnOutPGNFileListener());
        view.chcTimeControlType.addSelectionListener(getModelUpdaterListener());
        view.spnMatchRounds.addSelectionListener(getModelUpdaterListener());
        view.spnSimulCycles.addSelectionListener(getModelUpdaterListener());
        view.chcMainSimulEnginge.addSelectionListener(getModelUpdaterListener());
        view.tabFolder.addSelectionListener(getModelUpdaterListener());
        view.pbTrn.addPaintListener(getPbTrnPaintListener());
        view.getShell().addShellListener(tournamentLauncher.getShellClosedListener());
    }

    private void initViewers() {
        initPreConfEnginesViewer();
        initTrnEnginesViewer();
        initSimulViewer();
    }

    private void initTimeControlTypesCombo() {
        view.chcTimeControlType.setItems(Controller.timeControlTypes);
        Point size = view.chcTimeControlType.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        view.chcTimeControlType.setSize(size);
    }

    private void initTrnEnginesViewer() {
        view.trnEnginesViewer = new ListViewer(view.lstTrnEngines);
        view.trnEnginesViewer.setContentProvider(getTrnEnginesContentProvider());
        view.trnEnginesViewer.setInput(model.getCurrentTournament().getPlayersList());
    }

    private void initPreConfEnginesViewer() {
        view.preConfEnginesViewer = new ListViewer(view.lstPreconfEngines);
        view.preConfEnginesViewer.setContentProvider(getPreConfiguredEnginesContentProvider());
        view.preConfEnginesViewer.setInput(model.getPreconfEnginesLst());
    }

    private void initSimulViewer() {
        view.simulViewer = new ComboViewer(view.chcMainSimulEnginge);
        view.simulViewer.setContentProvider(getPreConfiguredEnginesContentProvider());
        view.simulViewer.setInput(model.getPreconfEnginesLst());
    }

    private IStructuredContentProvider getTrnEnginesContentProvider() {
        return new IStructuredContentProvider() {
            public void dispose() {
            }

            public Object[] getElements(Object arg0) {
                return model.getCurrentTournament().getPlayersList().toArray();
            }

            public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
            }
        };
    }

    private IStructuredContentProvider getPreConfiguredEnginesContentProvider() {
        return new IStructuredContentProvider() {
            public void dispose() {
            }

            public Object[] getElements(Object arg0) {
                return model.getPreconfEnginesLst().toArray();
            }

            public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
            }
        };
    }

    private SelectionAdapter getBtnAddEngineListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                ChessEngine engine = (ChessEngine) view.preConfEnginesViewer.getElementAt(model.getPreconfEnginesLst().size() - 1);
                if (engine == null || !engine.getName().equals("")) {
                    model.getPreconfEnginesLst().add(new ChessEngine("", "", ""));
                    view.preConfEnginesViewer.refresh();
                    view.getEngineSettings().getTxtEngineName().setText("");
                    view.getEngineSettings().getTxtEngineCmd().setText("");
                    view.getEngineSettings().getTxtEngineDir().setText("");
                }
                view.lstPreconfEngines.select(view.lstPreconfEngines.getItemCount() - 1);
            }
        };
    }

    private SelectionAdapter getBtnAddToTrnListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection selection = (IStructuredSelection) view.preConfEnginesViewer
                        .getSelection();
                ChessEngine engine = (ChessEngine) selection.getFirstElement();
                if (engine != null) {
                    model.getRoundRobinTournament().addPlayer(engine);
                    model.getSimulTournament().addPlayer(engine);
                    controller.loadViewFromModel();
                }
            }
        };
    }

    private SelectionAdapter getBtnBrowseEngineCmdListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(view.getShell(), SWT.OPEN);
                String file = dialog.open();
                if (file != null) {
                    view.getEngineSettings().getTxtEngineCmd().setText(file);
                }
            }
        };
    }

    private SelectionAdapter getBtnBrowseEngineDirListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                DirectoryDialog dialog = new DirectoryDialog(view.getShell(), SWT.OPEN);
                String directory = dialog.open();
                if (directory != null) {
                    view.getEngineSettings().getTxtEngineDir().setText(directory);
                }
            }
        };
    }

    private SelectionAdapter getBtnLoadListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(view.getShell(), SWT.OPEN);
                String file = dialog.open();
                if (file != null) {
                    Tournament trn;
                    try {
                        trn = (Tournament) Util.readFromFile(file);
                    }
                    catch (Exception ex) {
                        ErrorDialog.showError("Loading tournament failed!", view.getShell());
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Loading tournament failed!", ex);
                        return;
                    }
                    model.setCurrentTournament(trn);
                    controller.loadViewFromModel();
                }
            }
        };
    }

    private SelectionAdapter getBtnOutPGNFileListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(view.getShell(), SWT.SAVE);
                String file = dialog.open();
                if (file != null) {
                    view.txtOutPGNFile.setText(file);
                }
            }
        };
    }

    private SelectionAdapter getBtnPlayListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                view.getEngineSettings().getBtnPlay().setEnabled(false);
                final ChessEngine engine = new ChessEngine(
                        view.getEngineSettings().getTxtEngineName().getText(),
                        view.getEngineSettings().getTxtEngineCmd().getText(),
                        view.getEngineSettings().getTxtEngineDir().getText());
                Thread testEngineThread = new Thread() {
                    public void run() {
                        try {
                            XboardMatchRunner match = new XboardMatchRunner(engine, null);
                            match.run();
                        }
                        catch (RuntimeException ex) {
                            ErrorDialog.showError(ex.getMessage(), view.getShell());
                        }
                        finally {
                            view.getShell().getDisplay().asyncExec(new Runnable() {
                                public void run() {
                                    view.getEngineSettings().getBtnPlay().setEnabled(true);
                                }
                            });
                        }
                    }

                };
                testEngineThread.start();
            }
        };
    }

    private SelectionAdapter getBtnRemoveEngineListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                int selectionIndex = view.lstPreconfEngines.getSelectionIndex();
                if (selectionIndex > -1) {
                    model.getPreconfEnginesLst().remove(selectionIndex);
                    view.preConfEnginesViewer.refresh();
                    view.getEngineSettings().getTxtEngineName().setText("");
                    view.getEngineSettings().getTxtEngineCmd().setText("");
                    view.getEngineSettings().getTxtEngineDir().setText("");
                    try {
                        controller.saveModelFromView();
                    } catch (IOException e1) {
                        ErrorDialog.showError("saving failed", view.getShell());
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, "saving failed", e1);
                    }
                }
            }
        };
    }

    private SelectionAdapter getBtnRemoveFromTrnListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection selection = (IStructuredSelection) view.trnEnginesViewer.getSelection();
                TournamentChessEngine engine = (TournamentChessEngine) selection.getFirstElement();
                if (engine != null) {
                    model.getRoundRobinTournament().removePlayer(engine.getPairingNumber());
                    model.getSimulTournament().removePlayer(engine.getPairingNumber());
                    controller.loadViewFromModel();
                }
            }
        };
    }

    private SelectionAdapter getBtnSaveEngineListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                if ("".equals(view.getEngineSettings().getTxtEngineName().getText())
                        || "".equals(view.getEngineSettings().getTxtEngineCmd().getText())) {
                    ErrorDialog.showError("Specify name and command!", view.getShell());
                    return;
                }
                if (view.lstPreconfEngines.getSelectionIndex() == -1) {
                    model.getPreconfEnginesLst().add(
                            new ChessEngine(view.getEngineSettings().getTxtEngineName().getText(),
                                    view.getEngineSettings().getTxtEngineCmd().getText(),
                                    view.getEngineSettings().getTxtEngineDir().getText()));
                    view.preConfEnginesViewer.refresh();
                    view.lstPreconfEngines.select(view.lstPreconfEngines.getItemCount() - 1);
                }
                IStructuredSelection selection = (IStructuredSelection) view.preConfEnginesViewer.getSelection();
                ChessEngine engine = (ChessEngine) selection.getFirstElement();
                engine.setName(view.getEngineSettings().getTxtEngineName().getText());
                engine.setCommand(view.getEngineSettings().getTxtEngineCmd().getText());
                engine.setWorkingDirectory(view.getEngineSettings().getTxtEngineDir().getText());
                view.preConfEnginesViewer.refresh();
                try {
                    controller.saveModelFromView();
                } catch (IOException e1) {
                    ErrorDialog.showError("saving failed", view.getShell());
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "saving failed", e1);
                }
            }
        };
    }

    private SelectionAdapter getBtnSaveTrnAsListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(view.getShell(), SWT.SAVE);
                String file = dialog.open();
                if (file != null) {
                    controller.updateModelFromView();
                    try {
                        Util.saveToFile(model.getCurrentTournament(), file);
                    } catch (IOException e1) {
                        ErrorDialog.showError("saving failed", view.getShell());
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, "saving failed", e1);
                    }
                }
            }
        };
    }

    private SelectionAdapter getBtnSaveTrnListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                try {
                    controller.saveModelFromView();
                } catch (IOException e1) {
                    ErrorDialog.showError("saving failed", view.getShell());
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "saving failed", e1);
                }
            }
        };
    }

    private SelectionAdapter getModelUpdaterListener() {
        return new org.eclipse.swt.events.SelectionAdapter() {
            public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
                controller.updateModelFromView();
            }
        };
    }

    private SelectionAdapter getPreConfEnginesSelectedListener() {
        return new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection selection = (IStructuredSelection) view.preConfEnginesViewer.getSelection();
                ChessEngine engine = (ChessEngine) selection.getFirstElement();
                view.getEngineSettings().getTxtEngineName().setText(engine.getName());
                view.getEngineSettings().getTxtEngineCmd().setText(engine.getCommand());
                view.getEngineSettings().getTxtEngineDir().setText(engine.getWorkingDirectory());
            }
        };
    }

    private PaintListener getPbTrnPaintListener() {
        return new PaintListener() {
            public void paintControl(PaintEvent e) {
                String string = "";
                if (view.pbTrn.getMaximum() < Controller.MAX_GAMES) {
                    long percent = Math.round(view.pbTrn.getSelection() * 1.0 / (view.pbTrn.getMaximum() - view.pbTrn.getMinimum()) * 100);
                    string = percent + "% ";
                    string = string + view.pbTrn.getSelection() + "-" + view.pbTrn.getMaximum();
                }
                Point point = view.pbTrn.getSize();
                e.gc.setForeground(view.getShell().getDisplay().getSystemColor(SWT.COLOR_BLACK));
                FontMetrics fontMetrics = e.gc.getFontMetrics();
                int stringWidth = fontMetrics.getAverageCharWidth() * string.length();
                int stringHeight = fontMetrics.getHeight();
                e.gc.drawString(string, (point.x - stringWidth) / 2, (point.y - stringHeight) / 2, true);
            }
        };
    }

}
